/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package ricm4.ar.nio;

import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.nio.channels.spi.SelectorProvider;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
 *
 * @author Minyi, Xu
 */
public class NioClient implements Runnable {
    private InetAddress hostAddress;
    private int port;
    private Selector selector;
    private ByteBuffer outBuffer;
    private HashSet<ByteBuffer> buffers;
    //private HashMap<SocketChannel, HashSet<ByteBuffer>> mapChannelBuffers;
    private Map<SocketChannel, byte[]> serverMessage = new ConcurrentHashMap<>();
    private int length;
    private ByteBuffer inBuffer;
   
    
    public NioClient(InetAddress hostAddress, int port) throws IOException  {
        this.hostAddress = hostAddress;
        this.port = port;
        selector = SelectorProvider.provider().openSelector();

	SocketChannel socketChannel = SocketChannel.open();
	socketChannel.configureBlocking(false);
        socketChannel.register(selector, SelectionKey.OP_CONNECT);
        socketChannel.connect(new InetSocketAddress(this.hostAddress,this.port));
    }
    
    @Override
    public void run(){
        while(true){
            try{
                selector.select();
                Iterator selectedKeys = this.selector.selectedKeys().iterator();
                while(selectedKeys.hasNext()){
                    SelectionKey key = (SelectionKey)selectedKeys.next();
                    selectedKeys.remove();
                    if(!key.isValid()){
                        continue;
                    }
                    if(key.isConnectable()){
                        finishConnection(key);
			
                        System.out.println("server connected....");
                    }else if (key.isWritable()){
                        handleDataOut(key);
                    }else if (key.isReadable()){
                        handleDataIn(key);
                    }
                }
            }catch(IOException ex){
                Logger.getLogger(NioClient.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
    
    public static void main(String[] args) {
	try {
		new Thread(new NioClient(null, 8888)).start();
	}catch (IOException e) {
		Logger.getLogger(NioClient.class.getName()).log(Level.SEVERE, null, e);
	}
    }

    private void finishConnection(SelectionKey key) throws IOException {
	SocketChannel socketChannel = (SocketChannel) key.channel();
	try {
		socketChannel.finishConnect();
	} catch (IOException e) {
		System.out.println(e);
		key.cancel();
		return;
	}
	key.interestOps(SelectionKey.OP_WRITE);
    }
    
    private void handleDataOut(SelectionKey key) throws IOException {
	SocketChannel socketChannel = (SocketChannel) key.channel();
//	synchronized (this.mapChannelBuffers) {
//		buffers=mapChannelBuffers.get(socketChannel);
//		Iterator it=buffers.iterator();
//                while (!buffers.isEmpty()) {
//			ByteBuffer buf = (ByteBuffer) it.next();
//			it.remove();
//			socketChannel.write(buf);
//		}
//		if (buffers.isEmpty()) {
//			key.interestOps(SelectionKey.OP_READ);
//		}
//	}
	Scanner scanner=new Scanner(System.in);
	System.out.println("please input message:");
	String message = scanner.nextLine(); 
        sendData(socketChannel, message.getBytes()); 
        socketChannel.write(outBuffer); 
    }

    private void handleDataIn(SelectionKey key) throws IOException{
        SocketChannel socketChannel = (SocketChannel) key.channel();
	inBuffer = ByteBuffer.allocate(length);
        int numRead=-1;
	try {
		numRead = socketChannel.read(inBuffer);
	} catch (IOException e) {
		key.cancel();
		socketChannel.close();
		return;
	}
	if (numRead == -1) {
		key.channel().close();
		key.cancel();
		return;
	}
	this.processData(socketChannel, this.outBuffer.array(), numRead); 
    }
    
	private void sendData(SocketChannel socketChannel, byte[] data) throws IOException {
		ByteBuffer.wrap(data);
		SelectionKey key=socketChannel.keyFor(selector);
		key.interestOps(SelectionKey.OP_WRITE);
	}
    
	private void processData(SocketChannel socketChannel, byte[] data, int numRead) throws IOException {
		byte[] bytes = serverMessage.get(socketChannel); 
		if (bytes == null) { 
			bytes = new byte[0]; 
		} 
		if (numRead > 0) { 
			byte[] newBytes = new byte[bytes.length + numRead]; 
			System.arraycopy(bytes, 0, newBytes, 0, bytes.length); 
			System.arraycopy(data, 0, newBytes, bytes.length, numRead); 
			serverMessage.put(socketChannel, newBytes); 
		}
	}
}